/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.inspur.edp.cef.generator.repository.adaptor.method;

//import Inspur.Ecp.Caf.DataAccess.*;
//import Inspur.Gsp.Caf.Generator.BaseInfoItem.*;
//import Inspur.Gsp.Caf.Generator.ClassMethodGenerator.*;
//import Inspur.Gsp.Caf.Generator.Utils.*;
//import Inspur.Gsp.Cef.Repository.*;
//import Microsoft.CodeAnalysis.*;
//import Microsoft.CodeAnalysis.CSharp.*;
//import Microsoft.CodeAnalysis.CSharp.Syntax.*;

import com.inspur.edp.bef.bizentity.GspBizEntityObject;
import com.inspur.edp.caf.generator.baseInfo.AnnotationInfo;
import com.inspur.edp.caf.generator.baseInfo.ParameterInfo;
import com.inspur.edp.caf.generator.baseInfo.TypeInfo;
import com.inspur.edp.caf.generator.method.ClassMethodGenerator;
import com.inspur.edp.caf.generator.utils.ExpressionUtils;
import com.inspur.edp.cef.api.repository.GspDbDataType;
import com.inspur.edp.cef.designtime.api.IGspCommonDataType;
import com.inspur.edp.cef.designtime.api.IGspCommonField;
import com.inspur.edp.cef.designtime.api.element.GspElementObjectType;
import com.inspur.edp.cef.gencommon.MediateType;
import com.inspur.edp.cef.generator.coreGenerator.base.CefCoreGeneratorContext;
import com.inspur.edp.cef.generator.repository.RepositoryGenUtil;
import com.inspur.edp.cef.repository.dbcolumninfo.DbColumnInfo;
import com.inspur.edp.cef.repository.typetransprocesser.*;
import com.inspur.edp.das.commonmodel.IGspCommonElement;
import com.inspur.edp.das.commonmodel.IGspCommonObject;
import com.inspur.edp.lcm.databaseobject.api.DatabaseObjectServiceForWebIde;
import com.inspur.edp.udt.designtime.api.entity.ComplexDataTypeDef;
import com.inspur.edp.udt.designtime.api.entity.SimpleDataTypeDef;
import com.inspur.edp.udt.designtime.api.entity.UnifiedDataTypeDef;
import com.inspur.edp.udt.designtime.api.entity.dbInfo.ColumnMapType;
import com.inspur.edp.udt.designtime.api.entity.element.UdtElement;
import io.iec.edp.caf.commons.utils.SpringBeanUtils;
import io.iec.edp.caf.databaseobject.api.entity.DataType;
import io.iec.edp.caf.databaseobject.api.entity.DatabaseObjectColumn;
import io.iec.edp.caf.databaseobject.api.entity.DatabaseObjectTableCore;
import lombok.var;
import org.eclipse.jdt.core.dom.*;

import java.util.ArrayList;

public class InitColumnsGenerator extends ClassMethodGenerator {
	private IGspCommonDataType gspCommonDataType;
	private DatabaseObjectServiceForWebIde dboService;
	private CefCoreGeneratorContext cefGenerateContext;
	public InitColumnsGenerator(CefCoreGeneratorContext cefCoreGeneratorContext, IGspCommonDataType gspCommonDataType) {
		this.cefGenerateContext = cefCoreGeneratorContext;
		this.gspCommonDataType = gspCommonDataType;
	}

	@Override
	protected String getMethodName() {
		return "initColumns";
	}

	@Override
	protected TypeInfo getReturnType() {
		return new TypeInfo(void.class);
	}

	@Override
	protected ArrayList<Modifier.ModifierKeyword> getAccessModifiers() {
		return RepositoryGenUtil.createModifierKeywordArray(Modifier.ModifierKeyword.PROTECTED_KEYWORD);
	}

	@Override
	protected ArrayList<AnnotationInfo> getAttributeList() {
		return RepositoryGenUtil.buildOverrideAnnotationInfo(ast);
	}

	@Override
	protected ArrayList<ParameterInfo> getParameterCollection() {
		return null;
	}

	private DatabaseObjectServiceForWebIde getDboService() {
		if (dboService == null) {
			dboService = SpringBeanUtils.getBean(DatabaseObjectServiceForWebIde.class);
		}
		return dboService;
	}

	protected DatabaseObjectTableCore getDatabaseObject() {
		return (DatabaseObjectTableCore)
				getDboService()
						.getDatabaseObjectById(
								cefGenerateContext.getCefGenContext().getMetadataPath(),
								getBeObject().getRefObjectName());
	}

	private GspBizEntityObject getBeObject() {
		return (GspBizEntityObject) gspCommonDataType;
	}


	@Override
	protected Block buildMethodBody() {
		java.util.ArrayList<Statement> statements = new java.util.ArrayList<Statement>();
		DatabaseObjectTableCore dbo = getDatabaseObject();
		for (IGspCommonField element : gspCommonDataType.getContainElements()) {
			if (element.getIsVirtual() || element.getObjectType() == GspElementObjectType.DynamicProp) {
				continue;
			}
			if (element.getIsUdt()) {
				UnifiedDataTypeDef udt = (UnifiedDataTypeDef)cefGenerateContext.getCefGenContext().getMetadataJitContext().getMetadata(element.getUdtID()).getContent();
				if(udt instanceof SimpleDataTypeDef || (udt instanceof ComplexDataTypeDef && ((ComplexDataTypeDef)udt).getDbInfo().getMappingType() == ColumnMapType.SingleColumn)){
					var column = dbo.getColumnById(((IGspCommonElement) element).getColumnID());
					if (column == null) {
						throw new RuntimeException("dbo中找不到" + element.getLabelID() + "对应的列");
					}
					Statement statement = buildAddColumnStatement((IGspCommonElement) element, column);
					statements.add(statement);
				}
				else if(udt instanceof ComplexDataTypeDef && ((ComplexDataTypeDef)udt).getDbInfo().getMappingType() == ColumnMapType.MultiColumns){
					for(IGspCommonField child: element.getChildElements()){
						var column = dbo.getColumnById(((IGspCommonElement)child).getColumnID());
						if (column == null) {
							throw new RuntimeException("dbo中找不到" + child.getLabelID() + "对应的列");
						}
						// TODO 暂时没法只根据element判断是否是ChildElement
						child.setIsUdt(true);
						UdtElement udtElement=(UdtElement) udt.findElement(element.getMappingRelation().getMappingInfo(child.getID()));
						if(udtElement==null){
							throw new RuntimeException("没有找到多值存为多列udt上"+element.getLabelID()+"的字段！");
						}
						Statement statement = buildUdtAddColumnStatement((IGspCommonElement) child, (IGspCommonElement) element, column, udtElement);
						statements.add(statement);
					}
				}
//				buildUdtElementStatement((IGspCommonElement) element, dbo, statements);
			} else {
				var column = dbo.getColumnById(((IGspCommonElement) element).getColumnID());
				if (column == null) {
					throw new RuntimeException("dbo中找不到" + element.getLabelID() + "对应的列");
				}
				Statement statement = buildAddColumnStatement((IGspCommonElement) element, column);
				statements.add(statement);
			}
		}
//		for (DbColumnInfo column : dbColumns) {
//			Statement statement = buildAddColumnStatement(column);
//			statements.add(statement);
//		}
		Block block = ast.newBlock();
		block.statements().addAll(statements);
		return block;
	}

	private Statement buildAddColumnStatement(IGspCommonElement element, DatabaseObjectColumn dboColumn) {
		java.util.ArrayList arguments = buildArgumentList(element, element, dboColumn,false,null);
		String methodName = "addColumn";
		SuperMethodInvocation methodInvocation = ast.newSuperMethodInvocation();
		methodInvocation.setName(ast.newSimpleName(methodName));
		methodInvocation.arguments().addAll(arguments);
		return ast.newExpressionStatement(methodInvocation);
	}

	private Statement buildUdtAddColumnStatement(IGspCommonElement element, IGspCommonElement belongElement, DatabaseObjectColumn dboColumn, UdtElement udtElement) {
		java.util.ArrayList arguments = buildArgumentList(element, belongElement, dboColumn,true,udtElement);
		String methodName = "addColumn";
		SuperMethodInvocation methodInvocation = ast.newSuperMethodInvocation();
		methodInvocation.setName(ast.newSimpleName(methodName));
		methodInvocation.arguments().addAll(arguments);
		return ast.newExpressionStatement(methodInvocation);
	}

	private boolean isPrimaryKey(IGspCommonElement element) {
		return ((IGspCommonObject)this.gspCommonDataType).getColumnGenerateID().getElementID().equals(element.getID());
	}
	private boolean isParentColumn(IGspCommonElement element) {
		if (element.getBelongObject() != null && element.getBelongObject().getParentObject() != null) {
			return element.getBelongObject().getKeys().get(0).getSourceElement() .equals( element.getID());
		}
		return false;
	}

	private java.util.ArrayList<Expression> buildArgumentList(
			IGspCommonElement element, IGspCommonElement belongElement, DatabaseObjectColumn dboColumn, boolean isMultiColumns, UdtElement field) {
		java.util.ArrayList<Expression> argumentList = new java.util.ArrayList<Expression>();

		Expression propertyNameArgument = RepositoryGenUtil.getStringArgument(ast, element.getLabelID());
		argumentList.add(propertyNameArgument);

		Expression dbColumnArgument = RepositoryGenUtil.getStringArgument(ast, dboColumn.getCode());
		argumentList.add(dbColumnArgument);

//		Expression dataTypeArgument = RepositoryGenUtil.getEnumArgument(GspDbDataType.class, column.getColumnType().toString());
		var dataType = transDataType(dboColumn.getDataType());
		Expression dataTypeArgument = ExpressionUtils.getQualifiedName(ast, "GspDbDataType", String.valueOf(dataType.toString()));//RepositoryGenUtil.getNumberArgument(ast, String.valueOf(column.getColumnType().getValue()));
		argumentList.add(dataTypeArgument);
		addImport(GspDbDataType.class.getTypeName());

		Expression lengthArgument = RepositoryGenUtil.getNumberArgument(ast, String.valueOf(element.getLength()));
		argumentList.add(lengthArgument);

		Expression precisionArgument = RepositoryGenUtil.getNumberArgument(ast, String.valueOf(element.getPrecision()));
		argumentList.add(precisionArgument);


		//TODO 默认值处理问题，是否需要传入
		Expression defaultArgument = RepositoryGenUtil.getNullArgument(ast);
		argumentList.add(defaultArgument);


		Expression paramProcesser;
		if (isMultiColumns && field.getObjectType() == GspElementObjectType.Enum) {
			MediateType enumType = new MediateType(
					field.getBelongObject().getGeneratedEntityClassInfo().getClassNamespace(),
					field.getEnumTypeName());
			paramProcesser = getEnumTypeProcesser(enumType);
		} else if (element.getObjectType() == GspElementObjectType.Enum) {
			MediateType enumType = new MediateType(
					element.getBelongObject().getGeneratedEntityClassInfo().getClassNamespace(),
					element.getEnumTypeName());
			paramProcesser = getEnumTypeProcesser(enumType);
		} else {
			Class processer = getTypeProcesser(dataType, element);
			var invocation = ast.newMethodInvocation();
			invocation.setExpression(ast.newName(processer.getName()));
			invocation.setName(ast.newSimpleName("getInstacne"));
			paramProcesser = invocation;
		}

		argumentList.add(paramProcesser);

		Expression primaryKeyArgument = RepositoryGenUtil.getBoolArgumnet(ast, isPrimaryKey((IGspCommonElement) element));
		argumentList.add(primaryKeyArgument);


		Expression refElementArgument = RepositoryGenUtil.getBoolArgumnet(ast, element.getIsRef());
		argumentList.add(refElementArgument);


		Expression multiElementArgument = RepositoryGenUtil.getBoolArgumnet(ast, element.getIsMultiLanguage());
		argumentList.add(multiElementArgument);


		Expression udtElementArgument = RepositoryGenUtil.getBoolArgumnet(ast, element.getIsUdt());
		argumentList.add(udtElementArgument);


		Expression assElementArgument = RepositoryGenUtil.getBoolArgumnet(ast,element.getObjectType() == GspElementObjectType.Association);
		argumentList.add(assElementArgument);


		Expression enumElementArgument = RepositoryGenUtil.getBoolArgumnet(ast, element.getObjectType() == GspElementObjectType.Enum);
		argumentList.add(enumElementArgument);


		Expression delongEleArgument = RepositoryGenUtil.getStringArgument(ast, belongElement == null ? "" : belongElement.getLabelID());
		argumentList.add(delongEleArgument);


		Expression parentIdArgument = RepositoryGenUtil.getBoolArgumnet(ast, isParentColumn((IGspCommonElement) element));
		argumentList.add(parentIdArgument);

		return argumentList;
	}

	private java.util.ArrayList<Expression> buildArgumentList(DbColumnInfo column, IGspCommonField field) {
		java.util.ArrayList<Expression> argumentList = new java.util.ArrayList<Expression>();

		Expression propertyNameArgument = RepositoryGenUtil.getStringArgument(ast, column.getColumnName());
		argumentList.add(propertyNameArgument);

		Expression dbColumnArgument = RepositoryGenUtil.getStringArgument(ast, column.getDbColumnName());
		argumentList.add(dbColumnArgument);

//		Expression dataTypeArgument = RepositoryGenUtil.getEnumArgument(GspDbDataType.class, column.getColumnType().toString());

		Expression dataTypeArgument = ExpressionUtils.getQualifiedName(ast, "GspDbDataType", String.valueOf(column.getColumnType().toString()));//RepositoryGenUtil.getNumberArgument(ast, String.valueOf(column.getColumnType().getValue()));
		argumentList.add(dataTypeArgument);
		addImport(GspDbDataType.class.getTypeName());

		Expression lengthArgument = RepositoryGenUtil.getNumberArgument(ast, String.valueOf(column.getLength()));
		argumentList.add(lengthArgument);

		Expression precisionArgument = RepositoryGenUtil.getNumberArgument(ast, String.valueOf(column.getPrecision()));
		argumentList.add(precisionArgument);


		//TODO 默认值处理问题，是否需要传入
		Expression defaultArgument = RepositoryGenUtil.getNullArgument(ast);
		argumentList.add(defaultArgument);
		if (column.getTypeTransProcesser() != null) {
			Expression processerArgument;
			Enum2IntProcesser enum2IntProcesser = column.getTypeTransProcesser() instanceof Enum2IntProcesser ? (Enum2IntProcesser) column.getTypeTransProcesser():null;
			if (column.getIsEnum() && !column.getIsUdtElement() && enum2IntProcesser != null) {
				processerArgument = getEnumTypeProcesser(enum2IntProcesser.getEnumType());
			} else {
				if(column.getIsUdtElement() && column.getTypeTransProcesser() instanceof Enum2IntProcesser){

					processerArgument = getEnumTypeProcesser(enum2IntProcesser.getEnumType());
				}
				else {
					processerArgument = RepositoryGenUtil.createMethodInvocation(ast, ast.newName(column.getTypeTransProcesser().getClass().getTypeName()), "getInstacne", null);
				}

				}
			argumentList.add(processerArgument);
		} else {
			argumentList.add(RepositoryGenUtil.getNullArgument(ast));
		}


		Expression primaryKeyArgument = RepositoryGenUtil.getBoolArgumnet(ast, column.getIsPrimaryKey());
		argumentList.add(primaryKeyArgument);


		Expression refElementArgument = RepositoryGenUtil.getBoolArgumnet(ast, column.getIsAssociateRefElement());
		argumentList.add(refElementArgument);


		Expression multiElementArgument = RepositoryGenUtil.getBoolArgumnet(ast, column.getIsMultiLang());
		argumentList.add(multiElementArgument);


		Expression udtElementArgument = RepositoryGenUtil.getBoolArgumnet(ast, column.getIsUdtElement());
		argumentList.add(udtElementArgument);


		Expression assElementArgument = RepositoryGenUtil.getBoolArgumnet(ast, column.getIsAssociation());
		argumentList.add(assElementArgument);


		Expression enumElementArgument = RepositoryGenUtil.getBoolArgumnet(ast, column.getIsEnum());
		argumentList.add(enumElementArgument);


		Expression delongEleArgument = RepositoryGenUtil.getStringArgument(ast, column.getBelongElementLabel());
		argumentList.add(delongEleArgument);


		Expression parentIdArgument = RepositoryGenUtil.getBoolArgumnet(ast, column.getIsParentId());
		argumentList.add(parentIdArgument);

		return argumentList;
	}

	private GspDbDataType transDataType(DataType dataType) {
		GspDbDataType dbDataType = GspDbDataType.VarChar;
		switch (dataType) {
			case Char:
				dbDataType = GspDbDataType.Char;
				break;
			case Varchar:
				dbDataType = GspDbDataType.VarChar;
				break;
			case Blob:
				dbDataType = GspDbDataType.Blob;
				break;
			case DateTime:
				dbDataType = GspDbDataType.DateTime;
				break;
			case TimeStamp:
				dbDataType = GspDbDataType.DateTime;
				break;
			case Clob:
				dbDataType = GspDbDataType.Clob;
				break;
			case Int:
			case SmallInt:
				dbDataType = GspDbDataType.Int;
				break;
			case Decimal:
				dbDataType = GspDbDataType.Decimal;
				break;
			case NChar:
				dbDataType = GspDbDataType.NChar;
				break;
			case NVarchar:
				dbDataType = GspDbDataType.NVarChar;
				break;
			case NClob:
				dbDataType = GspDbDataType.NClob;
				break;
			case Boolean:
				dbDataType = GspDbDataType.Boolean;
				break;
		}
		return dbDataType;
	}

	private Expression getEnumTypeProcesser(MediateType enumType) {

		var vocation = ast.newClassInstanceCreation();
		vocation.setType(ast.newSimpleType(ast.newName(Enum2IntProcesser.class.getName())));
		var arg = ast.newTypeLiteral();
		arg.setType(ast.newSimpleType(ast.newName(enumType.getFullName())));
		vocation.arguments().add(arg);
		return vocation;
	}

	private java.lang.Class getTypeProcesser(
			GspDbDataType dbType, IGspCommonElement element) // GSPElementDataType elementType)
	{
		if (element.getObjectType() == GspElementObjectType.Enum) {
			switch (dbType) {
				case Int:
					return Enum2IntProcesser.class;
				case VarChar:
				case NVarChar:
					return Enum2VarcharProcesser.class;
			}

			throw new RuntimeException(
					"#GSPBefError#" + "不支持数据库字段类型为【" + dbType.toString() + "】的枚举字段使用" + "#GSPBefError#");
		}
		switch (element.getMDataType()) {
			case Boolean:
				if (dbType == GspDbDataType.Int) {
					return Bool2IntProcesser.class;
				}
				if (dbType == GspDbDataType.Char) {
					return Bool2CharProcesser.class;
				}
				if(dbType == GspDbDataType.Boolean) {
					return BoolTransProcesser.class;
				}
				break;
			case Date:
			case DateTime:
				return DateTimeTransProcesser.class;
			case Decimal:
				if (dbType == GspDbDataType.Decimal) {
					return DecimalTransProcesser.class;
				}
				break;
			case Integer:
				if (dbType == GspDbDataType.Int) {
					return IntTransProcesser.class;
				}
				break;
			case String:
				return VarcharTransProcesser.class;
			case Text:
				return ClobTransProcesser.class;
			case Binary:
				return BinaryTransProcesser.class;
		}

		throw new RuntimeException(
				"#GSPBefError#"
						+ "没有找到字段类型为"
						+ element.getMDataType().toString()
						+ "，数据库类型为"
						+ dbType.toString()
						+ "的转换器"
						+ "#GSPBefError#");
	}


	private Expression getEnumTypeProcesser(java.lang.Class enumType) {
		ClassInstanceCreation createInstance = ast.newClassInstanceCreation();
		createInstance.setType(new com.inspur.edp.caf.generator.baseInfo.TypeInfo(Enum2IntProcesser.class).getType(ast));
		addImport(Enum2IntProcesser.class.getTypeName());
		TypeLiteral enumTypeLiteral = ast.newTypeLiteral();
		enumTypeLiteral.setType(ast.newSimpleType(ast.newName(enumType.getTypeName())));
		createInstance.arguments().add(enumTypeLiteral);
		return createInstance;
	}
}
